package com.hskn.hss.aop;

import com.hskn.hss.constant.CommonConstant;
import com.hskn.hss.core.annotation.SysLog;
import com.hskn.hss.core.enums.BusinessType;
import com.hskn.hss.core.utils.StringUtils;
import com.hskn.hss.core.utils.spring.SpringUtils;
import com.hskn.hss.exception.HssException;
import com.hskn.hss.filter.BasicCodeDeleteHandler;
import com.hskn.hss.module.common.service.impl.CommonServiceImpl;
import com.hskn.hss.module.file.service.IFileService;
import com.hskn.hss.module.log.entity.Log;
import com.hskn.hss.module.log.service.impl.LogServiceImpl;
import com.hskn.hss.module.user.entity.User;
import com.hskn.hss.module.user.mapper.UserMapper;
import com.hskn.hss.utils.AddressUtils;
import com.hskn.hss.utils.BeanChangeUtil;
import com.hskn.hss.utils.JsonDateValueProcessor;
import com.hskn.hss.utils.Tools;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import net.sf.json.JsonConfig;
import org.apache.ibatis.session.SqlSession;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.*;

@Component
@Aspect
@Slf4j
public class LogAspect {

    private static final ThreadLocal<Long> startTime = new ThreadLocal<>();
    private static final ThreadLocal<HttpServletRequest> requestThreadLocal = new ThreadLocal<>();
    private static final ThreadLocal<String> requestBodyThreadLocal = new ThreadLocal<>();
    private static final ThreadLocal<String> requestMessage = new ThreadLocal<>();

    @Autowired
    private SqlSession sqlSession;
    @Autowired
    private IFileService fileService;

    /**
     * 业务逻辑方法切入点
     */
    @Pointcut("@annotation(com.hskn.hss.core.annotation.SysLog)")
    public void operateServiceCall() {

    }

    @Before(value = "operateServiceCall()")
    public void operateServiceCallBefore(JoinPoint joinPoint) {
        try {
            startTime.set(System.currentTimeMillis());
            HttpServletRequest req = Tools.getCurrentHttpServletRequest();

            String res = new String(new RequestReaderHttpServletRequestWrapper(req).getBody());
            requestBodyThreadLocal.set(res);
            requestThreadLocal.set(req);

            // 获得注解
            SysLog controllerSysLog = getAnnotationLog(joinPoint);
            if (controllerSysLog == null) {
                return ;
            }

            // 接口类型
            String businessType = controllerSysLog.businessType().toString();
            // 前端传过来的一级主键
            String idKey = controllerSysLog.idKey();
            // 一级名称(有二级时  作为二级的key )
            String names = controllerSysLog.names();

            // 1. 获取请求路径及IP地址
            StringBuffer requestURL = req.getRequestURL();
            System.out.println(requestURL.toString());

            // 2. 获取请求参数
            Object[] args = joinPoint.getArgs();
            log.info("args请求参数为{}", args);

            // 3. 获取登录用户信息
            JSONObject userInfo = JSONObject.fromObject(req.getSession().getAttribute("userInfo").toString());
            String account = userInfo.getString("account");
            String uid = userInfo.getString("uid");
            User user = SpringUtils.getBean(UserMapper.class).selectById(uid);

            if (controllerSysLog.isImport()){
                //获取上传图片
                MultipartResolver resolver = new CommonsMultipartResolver(req.getSession().getServletContext());
                MultipartHttpServletRequest multipartRequest = resolver.resolveMultipart(req);
                MultipartFile file = multipartRequest.getFile("file");

                String url = fileService.uploadFile(file);

                //获取表名
                String tableName = controllerSysLog.tableName();
                String tableComment = SpringUtils.getBean(CommonServiceImpl.class).getTableComment(tableName);

                String message = account + "批量导入" + "【" + tableComment + "】 文件路径： " + url + "##";

                requestMessage.set(message);
                return;
            }

            JSONObject params = JSONObject.fromObject(Tools.getBody(req));
            Object data = params.get("data");

            JSONObject dataJSONObject;
            if (Objects.nonNull(data)){
                dataJSONObject = JSONObject.fromObject(data);
            } else {
                dataJSONObject = params;
            }
            log.info("params请求参数为{}", params);

            //获取表名
            String tableName = controllerSysLog.tableName();
            String tableComment = SpringUtils.getBean(CommonServiceImpl.class).getTableComment(tableName);

            String message = "";
            if ("uid".equals(idKey) && Objects.isNull(dataJSONObject.get("uid"))){
                dataJSONObject.put("uid", uid);
            }

            if (StringUtils.isNotEmpty(idKey) && dataJSONObject.has(idKey)) {

                List<String> fieldList;
                String idKeyStr = "";
                if ("uid".equals(idKey)){
                    idKeyStr = userInfo.getString("uid");
                } else {
                    idKeyStr = dataJSONObject.getString(idKey);
                }

                if (idKeyStr.indexOf("[") > -1 && idKeyStr.indexOf("]") > -1){
                    fieldList = Tools.castList(dataJSONObject.get(idKey), String.class);
                } else {
                    fieldList = Arrays.asList(idKeyStr.split(","));
                }

                for (String field: fieldList) {

                    //获取数据库数据
                    JSONObject jsonResult = getBeanData(controllerSysLog.beanMapper().getName(), controllerSysLog.isId(), controllerSysLog.idKey(), field);
                    log.info("jsonResult请求参数为{}", jsonResult);

                    JSONObject beanJSONObject = new JSONObject();
                    //修改密码的 额外赋值
                    jsonResult.keySet().forEach(t -> {
                        if ("password".equals(t)){
                            if (Objects.nonNull(dataJSONObject.get("pwdNew"))){
                                beanJSONObject.put(t, dataJSONObject.get("pwdNew"));
                            }
                        } else {
                            if (StringUtils.isNotEmpty(String.valueOf(dataJSONObject.get(t)))){
                                beanJSONObject.put(t, dataJSONObject.get(t));
                            }
                        }
                    });
                    log.info("beanJSONObject请求参数为{}", beanJSONObject);

                    String[] nameList = names.split(",");
                    String objectName = "";

                    for (String name: nameList) {

                        // 没有账号的  赋值当前用户账号
                        if ("account".equals(name) && Objects.isNull(beanJSONObject.get("account"))){
                            beanJSONObject.put("account", account);
                        }

                        // 获取字段注释
                        String notes = getNotes(controllerSysLog.tableName(), name.trim());

                        // 获取二级数据mapper
                        String twoBeanMapper = controllerSysLog.twoBeanMapper().getName();

                        // 二级数据mapper不为空
                        if (!"java.lang.Void".equals(twoBeanMapper)){

                            // 获取二级数据库数据
                            JSONObject jsonResult1 = getBeanData(controllerSysLog.twoBeanMapper().getName(), controllerSysLog.twoIsId(), name, jsonResult.getString(name.trim()));

                            if (!jsonResult1.isNullObject()){

                                // 获取二级字段注释
                                String notes1 = getNotes(controllerSysLog.twoTableName(), controllerSysLog.twoNames().trim());

                                objectName += "-" + "〖" + notes1 + ":" + jsonResult1.getString(controllerSysLog.twoNames()) + "〗";
                            }
                        } else {
                            objectName += "-" + "〖" + notes + ":" + jsonResult.getString(name.trim()) + "〗";
                        }
                        objectName = objectName.substring(1);
                    }

                    // 更新的接口
                    if (BusinessType.UPDATE.toString().equals(businessType)){

                        // 比对两个对象的差异
                        BeanChangeUtil t = new BeanChangeUtil<>();
                        String str = t.contrastJSONObj(controllerSysLog.tableName(), idKey, jsonResult, beanJSONObject, controllerSysLog.isCompare());

                        if (str.equals("")) {
                            str = " (未有改变)";
                            System.out.println("(未有改变)");
                        } else {
                            str = " (" + str + ")";
                            System.out.println("(" + str + ")");
                        }

                        String message1 = account + "修改" + "【" + tableComment + "】" + objectName;
                        message1 = message1 + str + "##";

                        message += message1;

                    // 删除的接口
                    } else if (BusinessType.DELETE.toString().equals(businessType)) {

                        String message1 = account + "删除" + "【" + tableComment + "】" + objectName + "##";
                        message += message1;
                    }
                }

            } else {
                String jsonParamStr = "";
                String[] nameList = names.split(",");

                dataJSONObject.put("createUid", uid);
                dataJSONObject.put("createName", user.getName());

                for (String name: nameList) {

                    if ("account".equals(name) && Objects.isNull(dataJSONObject.get("account"))){
                        dataJSONObject.put("account", account);
                    }

                    String twoBeanMapper = controllerSysLog.twoBeanMapper().getName();

                    if (!"java.lang.Void".equals(twoBeanMapper)){

                        // 获取二级数据库数据
                        JSONObject jsonResult1 = getBeanData(controllerSysLog.twoBeanMapper().getName(), controllerSysLog.twoIsId(), name, dataJSONObject.getString(name.trim()));

                        if (!jsonResult1.isNullObject()){

                            // 获取二级字段注释
                            String notes = getNotes(controllerSysLog.twoTableName(), controllerSysLog.twoNames().trim());

                            if (StringUtils.isEmpty(jsonParamStr)){
                                jsonParamStr = "〖" + notes + ":" + jsonResult1.getString(controllerSysLog.twoNames()) + "〗";
                            } else {
                                jsonParamStr += "-" + "〖" + notes + ":" + jsonResult1.getString(controllerSysLog.twoNames()) + "〗";
                            }
                        }

                    } else {

                        //获取一级字段注释
                        String notes = getNotes(controllerSysLog.tableName(), name.trim());

                        if (StringUtils.isEmpty(jsonParamStr)){
                            jsonParamStr = "〖" + notes + ":" + JSONObject.fromObject(dataJSONObject).getString(name.trim()) + "〗";
                        } else {
                            jsonParamStr += "-" + "〖" + notes + ":" + JSONObject.fromObject(dataJSONObject).getString(name.trim()) + "〗";
                        }
                    }
                }
                message = account + "新增" + "【" + tableComment + "】" + jsonParamStr + "##";
            }
            //这样就去掉可最后两个符号##
            if (message.length() >= 2){
                message = message.substring(0,message.length() -2);
            }
            requestMessage.set(message);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 业务操作日志统一处理(后置通知)
     *
     * @param joinPoint 获取连接点传入信息
     */
    @AfterReturning(value = "operateServiceCall()", returning = "result")
    //@Around(value = "operateServiceCall()",argNames = "joinPoint")
    public Object operateServiceCallCallsAfter(JoinPoint joinPoint,Object result) {

        JSONObject jsonResult = JSONObject.fromObject(result);
        if (CommonConstant.SC_OK_200 == Integer.parseInt(jsonResult.getString("code"))){

//        // spring计时器
//        StopWatch stopWatch = new StopWatch();
//        stopWatch.start();

            // 1. 获取请求路径及IP地址
            HttpServletRequest req = requestThreadLocal.get();

            StringBuffer requestURL = req.getRequestURL();
            String ip = Tools.getIpAddr(req);
            // 解析IP地址
            AddressUtils addressUtils = new AddressUtils();
            String address = addressUtils.getAddresses("ip=" + ip, "GBK");

            // 2. 获取请求参数
            Object[] args = joinPoint.getArgs();
//            log.info("请求参数为{}", args);
            String jsonArg = requestBodyThreadLocal.get();

            // 3. 获取方法相关信息
            String methodName = joinPoint.getSignature().getName();
            // 获取类名
            String target = joinPoint.getTarget().toString();
            String serviceName = target.substring(0, target.indexOf("@"));

            // 4. 获取登录用户信息
            JSONObject userInfo = JSONObject.fromObject(req.getSession().getAttribute("userInfo").toString());
            String uid = userInfo.getString("uid");
            String account = userInfo.getString("account");

//            log.info("响应结果为{}", result);
            Log sysLog = new Log();
            sysLog.setIp(ip);
            sysLog.setAddress(address);
            sysLog.setUid(uid);
            sysLog.setAccount(account);
            sysLog.setContent(requestMessage.get());
            sysLog.setCostTime(System.currentTimeMillis() - startTime.get());
            sysLog.setReqUrl(requestURL.toString().split("hss")[1]);
            sysLog.setReqParam(jsonArg);
            sysLog.setClassName(serviceName);
            sysLog.setMethodName(methodName);
            sysLog.setRespValue(result.toString());
            SpringUtils.getBean(LogServiceImpl.class).save(sysLog);
            startTime.remove();
            requestBodyThreadLocal.remove();
        }
        return result;
    }

    /**
     * 是否存在注解，如果存在就获取
     */
    private SysLog getAnnotationLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (method != null) {
            return method.getAnnotation(SysLog.class);
        }
        return null;
    }

    /**
     * 获取数据库数据
     */
    private JSONObject getBeanData(String beanMapperName, boolean isId, String key, String keyValue) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {

        // 获得注解
//        SysLog controllerSysLog = getAnnotationLog(joinPoint);

//        String bean = controllerSysLog.bean().getName();
//        Class<?> beanClass = null;
//        try {
//            beanClass = Class.forName(bean);
//        } catch (ClassNotFoundException e) {
//            e.printStackTrace();
//        }

//        String beanMapper = controllerSysLog.beanMapper().getName();

        Class<?> beanMapperClass = null;
        try {
            beanMapperClass = Class.forName(beanMapperName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        // 获取Mapper地址
        Object beanMapperObject = Proxy.newProxyInstance(
                beanMapperClass.getClassLoader(),
                new Class[]{beanMapperClass},
                new BasicCodeDeleteHandler(sqlSession.getMapper(beanMapperClass))
        );

        Object beanData = null;
        JSONObject jsonResult = null;

        // 是主键的
        if (isId){
            Method method = beanMapperClass.getMethod("selectById", Serializable.class);
            beanData = method.invoke(beanMapperObject, keyValue);
        } else { // 不是主键的
            Method method = beanMapperClass.getMethod("selectByMap", Map.class);
            Map map = new HashMap();
            map.put(key, keyValue);
            Object objectList = method.invoke(beanMapperObject, map);

            List<Object> result = new ArrayList<>();
            if (objectList instanceof ArrayList<?>){
                for (Object o : (List<?>) objectList){
                    result.add(Object.class.cast(o));
                }
            }
            if (result.size() > 0){
                beanData = result.get(0);
            }
        }

        if (Objects.nonNull(beanData)){
            //格式化时间
            JsonConfig config = new JsonConfig();
            config.registerJsonValueProcessor(Date.class, new JsonDateValueProcessor());
            jsonResult = JSONObject.fromObject(beanData, config);
        }
        return jsonResult;
    }

    /**
     * 获取字段注释
     */
    private String getNotes(String tableName, String field) {
        //获取字段注释
        String columnName1 = StringUtils.isEmpty(field)?field:field.replaceAll("[A-Z]", "_$0").toLowerCase();
        String columnComment = SpringUtils.getBean(CommonServiceImpl.class).getColumnComment(tableName, columnName1);
        if (StringUtils.isEmpty(columnComment)){
            columnComment = SpringUtils.getBean(CommonServiceImpl.class).getColumnComment(tableName, field);
        }
        return columnComment;
    }


}
